package net.gdface.cli;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.TypeHandler;

/**
 * 参数配置抽象类
 * @author guyadong
 *
 */
public abstract class AbstractConfiguration extends Context implements CommonCliConstants, CmdConfig {
	/**
	 * 是否使用 trace 选项，为{@code true}时会将'{@code -X --trace}'自动添加到命令行参数定义中
	 */
	protected final boolean withTrace;
	@SuppressWarnings("serial")
	protected final Set<String> CONTROL_OPTIONS = new HashSet<String>() {
		{
			this.add(HELP_OPTION);
			this.add(HELP_OPTION_LONG);
			this.add(DEFINE_OPTION);
		}
	};
	private final Map<String, PropertyDescriptor> fields;
	/** 子类提供命令行参数的默认值 */
	protected abstract Map<String, Object> getDefaultValueMap();
	protected AbstractConfiguration() {
		this(false);
	}
	/**
	 * 构造方法<br>
	 * @param withTrace 是否使用 trace 选项，为{@code true}时会将'{@code -X --trace}'自动添加到命令行参数定义中
	 */
	protected AbstractConfiguration(boolean withTrace) {
		this.withTrace = withTrace;
		if(withTrace){
			CONTROL_OPTIONS.add(TRACE_OPTION);
			CONTROL_OPTIONS.add(TRACE_OPTION_LONG);
		}
        fields = initFields();
	}
	@Override
	public void loadConfig(Options options, CommandLine cmd) throws ParseException {
		Iterator<Option> it = options.getOptions().iterator();
		Option opt;
		String key;
		Map<String, Object> defaultMap = getDefaultValueMap();
		if (defaultMap == null){
			defaultMap = Collections.emptyMap();
		}
		while (it.hasNext()) {
			opt = it.next();
			// 优先用长值
			key = opt.getLongOpt() == null ? opt.getOpt() : opt.getLongOpt();
			if(isNeedOption(key)){				
				Object value = null;
				try{
					if(opt.hasArg()){
						if(cmd.hasOption(key)){
							if(opt.hasOptionalArg() && cmd.getOptionValues(key) == null){
								// NOTE_1: 有可选参数的选项视为true
								value = Boolean.TRUE;
							}else	if(opt.getArgs() == 1){
								value = cmd.getParsedOptionValue(key);
							}else{
								// 当选项允许多个参数时返回T[]
								String[] values = cmd.getOptionValues(key);
							
								if(values != null){
									// 根据选项的类型创建数组
									value = Array.newInstance((Class<?>) opt.getType(), values.length);
									for(int i = 0; i < values.length; ++i){	
										Array.set(value, i, getParsedOptionValue(opt,values[i]));
									}
								}
							}
						}
					}else{
						// 不需要参数的选项视为boolean,直接返回boolean值
						value = Boolean.valueOf(cmd.hasOption(key)); 
					}
				}catch(NoClassDefFoundError e){
					System.out.printf("key=%s %s\n",key,cmd.getOptionValue(key));	
					throw e;
				}

				if (null == value) {
					if (opt.isRequired()) {
						throw new IllegalArgumentException(String.format("%s or %s not define", opt.getOpt(),
								opt.getLongOpt()));
					} else if (opt.hasOptionalArg()) {
						// DO NOTHING
						// 有可选参数的选项不需要定义默认值
					}else{
						// 没有定义缺省值则抛出异常,缺省值可以为null
						if (!defaultMap.containsKey(key)) {
							throw new IllegalArgumentException(String.format("%s or %s not default value", opt.getOpt(),
									opt.getLongOpt()));
						}
						value = defaultMap.get(key);
					}
				}else if(opt.hasOptionalArg() && Boolean.TRUE.equals(value) && defaultMap.containsKey(key)){
					// 响应前面的NOTE_1, 
					// 如果提供了默认值，而命令行没有提供可选参数则使用默认值
					value = defaultMap.get(key);
				}
				this.setProperty(key, value);
			}
		}
	}
	private final boolean isNeedOption(String opt){
		return !CONTROL_OPTIONS.contains(opt);
	}
    /**
     * 根据Option的类型解析指定的字符串值
     * @param option 
     * @param value
     * @return Option定义类型的实例
     * @throws ParseException
     */
    private static Object getParsedOptionValue(Option option,String value) throws ParseException {
        
        if (option == null || value == null)
        {
            return null;
        }
        
        return TypeHandler.createValue(value, option.getType());
    }
    private Map<String, PropertyDescriptor> initFields(){
		BeanInfo beanInfo = null;
	    try {
	        beanInfo = Introspector.getBeanInfo(getClass());
	    } catch (IntrospectionException e) {
	        // no descriptors are added to the context
	        throw new RuntimeException(e);
	    }
	    PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
	    
	    Map<String, PropertyDescriptor>map = new HashMap<>();
	    if (descriptors != null) {
	        for(PropertyDescriptor pd: descriptors){
	        	Method method = pd.getReadMethod();
	        	// 过滤掉Object中的get方法
	    		if (method != null && method.getDeclaringClass() != Object.class) {
	    			map.put(pd.getName(), pd);
	    		}
	        }
	    }
	    return map;
	}
	/**
	 * 返回指定字段的值
	 * @param name 字段名
	 * @return 字段值,字段名无效返回{@code null}
	 */
	@SuppressWarnings("unchecked")
	public <T> T valueOf(String name){
    	PropertyDescriptor descriptor = fields.get(name);
    	if(null == descriptor){
    		return null;
    	}
    	try {
			return (T) descriptor.getReadMethod().invoke(this);
		} catch (Exception e) {
			if (e instanceof RuntimeException) {
			      throw (RuntimeException) e;
			    }
			throw new RuntimeException(e);
		}
    }
}
